package com.smartdoc.mojo;


import com.alibaba.fastjson.JSONObject;
import com.power.doc.model.ApiAllData;
import com.power.doc.model.ApiDoc;
import com.power.doc.model.ApiMethodDoc;
import com.smartdoc.config.ShowDocConfig;
import com.smartdoc.entity.ShowDoc;
import okhttp3.*;
import org.apache.maven.plugins.annotations.*;
import org.codehaus.plexus.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Execute(phase = LifecyclePhase.COMPILE)
@Mojo(name = "showdoc", requiresDependencyResolution = ResolutionScope.COMPILE)
public class ShowDocMojo extends JsonDocMojo {


    private static final OkHttpClient client;

    static {
        client = new OkHttpClient.Builder()
                .followRedirects(true)
                .followSslRedirects(true)
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .build();
    }

    @Parameter(property = "configs", required = true)
    private List<ShowDocConfig> configs;


    private ShowDoc buildOuterShowDoc(ApiAllData apiAllData, ApiDoc apiDoc, ApiMethodDoc apiMethodDoc){
        ShowDoc showDoc = new ShowDoc();
        showDoc.setNumber(apiMethodDoc.getOrder());

        StringBuilder catName = new StringBuilder();
        String project = apiAllData.getProjectName();
        catName.append(project).append("/");
        String version = apiAllData.getVersion();
        if (StringUtils.isEmpty(version)) {
            version = "通用版本";
        }

        catName.append(version).append("/");
        String apiDocShowName = apiDoc.getShowName();
        if(StringUtils.isEmpty(apiDocShowName)){
            apiDocShowName = apiDoc.getName();
        }

        catName.append(apiDocShowName).append("/");

        showDoc.setCatName(catName.toString());

        String apiShowName = apiMethodDoc.getShowName();
        if(StringUtils.isEmpty(apiShowName)){
            apiShowName = apiDoc.getName();
        }

        showDoc.setTitle(apiShowName);

        showDoc.setGroups(apiMethodDoc.getExtendProjects());

        StringBuilder docMd = new StringBuilder();
        docMd.append("**请求URL：** \n\n");
        docMd.append("- ` " + apiMethodDoc.getUrl() + " ` \n\n");
        docMd.append("**作者：** \n\n");
        docMd.append("- " + apiMethodDoc.getAuthor() + " \n\n");
        docMd.append("**接口描述：** \n\n");
        docMd.append("- " + apiMethodDoc.getDesc() + " \n\n");
        docMd.append("**请求方式：** \n\n");
        docMd.append("- " + apiMethodDoc.getType().toUpperCase() + " \n\n");
        docMd.append("**过时说明：** \n\n");
        if(apiMethodDoc.isDeprecated()){
            docMd.append("- 接口已过时,请使用新的接口 \n\n");
        }
        docMd.append("**请求头结构：** \n\n");
        docMd.append("|参数名|必选|类型|版本|说明| \n");
        docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
        docMd.append("|token |是  |string  |- |授权token  | \n");



        if (apiMethodDoc.getRequestParams() != null && apiMethodDoc.getRequestParams().size() > 0) {
            docMd.append("**请求参数：** \n\n");
            docMd.append("|参数名|必选|类型|版本|说明| \n");
            docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
            apiMethodDoc.getRequestParams().forEach(q -> {
                docMd.append("|" + q.getField() + " |" + (q.isRequired() ? "是" : "否") + "  |" + q.getType().toLowerCase() + "  |" + (q.getVersion() == null ? "-" : q.getVersion()) + " |" + q.getDesc() + "   | \n");
            });

            docMd.append("**请求参数示例：** \n\n");
            docMd.append(apiMethodDoc.getRequestExample()).append("\n\n");
        }

        if (apiMethodDoc.getResponseParams() != null && apiMethodDoc.getResponseParams().size() > 0) {
            docMd.append("**响应数据结构：** \n\n");
            docMd.append("|参数名|必选|类型|版本|说明| \n");
            docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
            apiMethodDoc.getResponseParams().forEach(q -> {
                docMd.append("|" + q.getField() + " |" + (q.isRequired() ? "是" : "否") + "  |" + q.getType().toLowerCase() + "  |" + (q.getVersion() == null ? "-" : q.getVersion()) + " |" + q.getDesc() + "   | \n");
            });
            docMd.append("**响应数据示例：** \n\n");
            docMd.append(apiMethodDoc.getResponseUsage()).append("\n\n");
        }

        docMd.append("**请求示例：** \n\n");
        docMd.append(apiMethodDoc.getRequestExample()).append("\n\n");


        showDoc.setContent(docMd.toString());
        return showDoc;
    }

    private ShowDoc builderInnerShowDoc(ApiAllData apiAllData, ApiDoc apiDoc, ApiMethodDoc apiMethodDoc){
        ShowDoc showDoc = new ShowDoc();
        showDoc.setNumber(apiDoc.getOrder());


        String showName = apiDoc.getShowName();
        if(StringUtils.isEmpty(showName)){
            showName = apiDoc.getName();
        }
        showDoc.setTitle(showName + " -- 内部API列表 --");
        StringBuilder catName = new StringBuilder();
        String project = apiAllData.getProjectName();

        catName.append(project).append("/");

        String version = apiAllData.getVersion();
        if (StringUtils.isEmpty(version)) {
            version = "通用版本";
        }

        catName.append(version).append("/");

        catName.append(showName);

        showDoc.setCatName(catName.toString());

        StringBuilder docMd = new StringBuilder();
        docMd.append("**简要描述：** \n\n")
                .append("- " + (apiDoc.getDesc() == null ? "" : apiDoc.getDesc()))
                .append("\n\n");
        docMd.append("***服务URL：*** \n\n");
        docMd.append("- ` " + apiMethodDoc.getPath() + " ` \n\n");
        docMd.append("**作者：** \n\n");
        docMd.append("- " + apiMethodDoc.getAuthor() + " \n\n");
        docMd.append("**接口描述：** \n\n");
        docMd.append("- " + apiMethodDoc.getDesc() + " \n\n");
        docMd.append("**请求方式：** \n\n");
        docMd.append("- " + apiMethodDoc.getType().toUpperCase() + " \n\n");
        docMd.append("**过时说明：** \n\n");
        if(apiMethodDoc.isDeprecated()){
            docMd.append("- 接口已过时,请使用新的接口 \n\n");
        }
        if (apiMethodDoc.getRequestHeaders() != null && apiMethodDoc.getRequestHeaders().size() > 0) {
            docMd.append("**内部服务请求头结构：** \n\n");
            docMd.append("|参数名|必选|类型|版本|说明| \n");
            docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
            apiMethodDoc.getRequestHeaders().forEach(q -> {
                docMd.append("|" + q.getName() + " |" + (q.isRequired() ? "是" : "否") + "  |" + q.getType().toLowerCase() + "  |" + (q.getSince() == null ? "-" : q.getSince()) + " |" + q.getDesc() + "   | \n");
            });
        }

        if (apiMethodDoc.getRequestParams() != null && apiMethodDoc.getRequestParams().size() > 0) {
            docMd.append("**请求参数：** \n\n");
            docMd.append("|参数名|必选|类型|版本|说明| \n");
            docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
            apiMethodDoc.getRequestParams().forEach(q -> {
                docMd.append("|" + q.getField() + " |" + (q.isRequired() ? "是" : "否") + "  |" + q.getType().toLowerCase() + "  |" + (q.getVersion() == null ? "-" : q.getVersion()) + " |" + q.getDesc() + "   | \n");
            });

            docMd.append("**请求参数示例：** \n\n");
            docMd.append(apiMethodDoc.getRequestExample()).append("\n\n");
        }

        if (apiMethodDoc.getResponseParams() != null && apiMethodDoc.getResponseParams().size() > 0) {
            docMd.append("**响应数据结构：** \n\n");
            docMd.append("|参数名|必选|类型|版本|说明| \n");
            docMd.append("|:----    |:---|:----- |:----- |-----   | \n");
            apiMethodDoc.getResponseParams().forEach(q -> {
                docMd.append("|" + q.getField() + " |" + (q.isRequired() ? "是" : "否") + "  |" + q.getType().toLowerCase() + "  |" + (q.getVersion() == null ? "-" : q.getVersion()) + " |" + q.getDesc() + "   | \n");
            });
            docMd.append("**响应数据示例：** \n\n");
            docMd.append(apiMethodDoc.getResponseUsage()).append("\n\n");
        }

        docMd.append("**请求示例：** \n\n");
        docMd.append(apiMethodDoc.getRequestExample()).append("\n\n");

        showDoc.setContent(docMd.toString());

        return showDoc;
    }

    private List<ShowDoc> buildShowDoc(ApiAllData apiAllData) {
        List<ShowDoc> showDocs = new ArrayList<>();
        if (apiAllData.getApiDocList() != null) {
            int i = 1;
            for(ApiDoc apiDoc : apiAllData.getApiDocList()){

                if(apiDoc.getList() != null){
                    for(ApiMethodDoc apiMethodDoc : apiDoc.getList()){
                        ShowDoc outerDoc = buildOuterShowDoc(apiAllData, apiDoc, apiMethodDoc);
//                        ShowDoc innerDoc = builderInnerShowDoc(apiAllData, apiDoc, apiMethodDoc);
                        showDocs.add(outerDoc);
//                        showDocs.add(innerDoc);
                        i++;
                    }
                }

            }
        }
        return showDocs;
    }

    @Override
    protected void saveDoc(ApiAllData apiAllData) throws Exception {
        Map<String, List<ShowDocConfig>> showDocConfigMap = new HashMap<>();
        if(configs == null || configs.size() == 0){
            return;
        }
        List<ShowDocConfig> defaultShowDocConfigs = new ArrayList<>();
        Set<String> apiKeys = new HashSet<>();
        for(ShowDocConfig v : configs){
            if(StringUtils.isEmpty(v.getApiKey())){
                throw new Exception("Showdoc APIKEY can not be empty!");
            }

            if(StringUtils.isEmpty(v.getApiToken())){
                throw new Exception("Showdoc APITOKEN can not be empty!");
            }

            if(apiKeys.contains(v.getApiKey())){
                throw new Exception("Showdoc ApiKey has duplicate!");
            }

            if(StringUtils.isEmpty(v.getGroup())){
                defaultShowDocConfigs.add(v);
            }else{
                String group = v.getGroup();
                if(!showDocConfigMap.containsKey(group)){
                    showDocConfigMap.put(group, new ArrayList<>());
                }
                showDocConfigMap.get(group).add(v);
            }
            apiKeys.add(v.getApiKey());
        }
        List<ShowDoc> showDocs = buildShowDoc(apiAllData);
        for (ShowDoc showDoc : showDocs) {
            System.out.println(JSONObject.toJSONString(showDoc));
            List<String> groups = showDoc.getGroups();
            List<ShowDocConfig> showDocConfigs = new ArrayList<>();
            if(groups != null && groups.size() > 0){
                groups.forEach(v -> {
                    if(!StringUtils.isEmpty(v)){
                        List<ShowDocConfig> temp = showDocConfigMap.get(v);
                        if(temp != null) {
                            showDocConfigs.addAll(temp);
                        }
                    }
                });
            }
            for(ShowDocConfig showDocConfig : defaultShowDocConfigs){
                uploadShowDoc(showDocConfig, showDoc);
            }

            for (ShowDocConfig showDocConfig : showDocConfigs){
                uploadShowDoc(showDocConfig, showDoc);
            }
        }

    }


    private void uploadShowDoc(ShowDocConfig showDocConfig, ShowDoc showDoc) throws Exception {


        String url;
        if(!StringUtils.isEmpty(showDocConfig.getHost()) && !showDocConfig.getHost().contains("www.showdoc.cc")){
            url = showDocConfig.getHost() + "/server/index.php?s=/api/item/updateByApi";
        }else{
            url = "https://www.showdoc.cc/server/api/item/updateByApi";
        }


        JSONObject param = new JSONObject();
        param.put("api_key", showDocConfig.getApiKey());
        param.put("api_token", showDocConfig.getApiToken());
        param.put("cat_name", showDoc.getCatName());
        param.put("page_title", showDoc.getTitle());
        param.put("page_content", showDoc.getContent());
        param.put("s_number", showDoc.getNumber());

        Headers.Builder headerBuilder = new Headers.Builder();
        headerBuilder.set("Content-Type", "application/json");

        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), JSONObject.toJSONBytes(param));

        Request postRequest = new Request.Builder()
                .url(url)
                .headers(headerBuilder.build())
                .post(requestBody)
                .build();

        Response response = client.newCall(postRequest).execute();
        boolean successful = response.isSuccessful();

        if (!successful) {
            int code = response.code();
            throw new Exception("request did not executed successfully,respond code is " + code);
        }
//        getLog().info("API-TITLE: " + showDoc.getTitle() + "; response: " + response.body().string());
    }


}
